#include "model.h"
#include <qgl.h>
using namespace std;
#include <QMainWindow>
#include "../attrib/attribute.h"
#include "../mesh/mesh.h"
#include "../mesh/elementgroup.h"


//-------------------------------------------------------------------------
Model::Model()
: m_mesh( NULL )
, m_scaleAttribSymbol( 1.0 )
{

}
//-------------------------------------------------------------------------


//-------------------------------------------------------------------------
void
Model::update( HEDModel _model )
{
  m_domains.clear();
  // creates the domains and stores a reference to each face of the model
  vector<Face*> surfaces = _model.getFaces();
  for( unsigned int s=0; s<surfaces.size()-1; s++)
  {
    Domain* domain = new Domain( surfaces[s] );
    m_domains.push_back( domain );
  }

}
//-------------------------------------------------------------------------


//-------------------------------------------------------------------------
void
Model::viewBoundaryMesh()
{
  // obtains the boundary points of each domain
  glPointSize(4.0f);
  list<Domain*>::iterator itD;
  for( itD = m_domains.begin(); itD != m_domains.end(); itD++ )
  {
    list<Boundary*> bounds;
    (*itD)->getBoundaries(bounds);
    list<Boundary*>::iterator itB;
    for( itB = bounds.begin(); itB != bounds.end(); itB++ )
    {
      vector<Point> sPt = (*itB)->getSubdivisionPoints();
      glBegin(GL_POINTS);
      for( unsigned int i=1; i<sPt.size()-1; i++)
      {
          glVertex3d(sPt[i].getX(),sPt[i].getY(),0.0);
      }
      glEnd();
    }
  }
}
//-------------------------------------------------------------------------


//-------------------------------------------------------------------------
void
Model::viewDomainMesh()
{
  // obtains the mesh points of each domain
  glLineWidth(1.0);
  list<Domain*>::iterator itD;
  for( itD = m_domains.begin(); itD != m_domains.end(); itD++ )
  {
    vector<vector<int> > t = (*itD)->getTrianglesIncidences();
    vector<Point> pts = (*itD)->getTrianglesPoints();
    for( int i = 0; i < t.size(); i++ )
    {
      glBegin(GL_LINE_LOOP);
      glVertex3d(pts[t[i][0]].getX(), pts[t[i][0]].getY(), 0.0);
      glVertex3d(pts[t[i][1]].getX(), pts[t[i][1]].getY(), 0.0);
      glVertex3d(pts[t[i][2]].getX(), pts[t[i][2]].getY(), 0.0);
      glEnd();
    }
  }
}
//-------------------------------------------------------------------------


//-------------------------------------------------------------------------
void
Model::viewBoundaryAttributes()
{
  double scale = 0.2*m_scaleAttribSymbol;
  // obtains the boundary points of each domain
  glLineWidth(1.0);
  list<Domain*>::iterator itD;
  for( itD = m_domains.begin(); itD != m_domains.end(); itD++ )
  {
    list<Boundary*> bounds;
    (*itD)->getBoundaries(bounds);
    list<Boundary*>::iterator itB;
    for( itB = bounds.begin(); itB != bounds.end(); itB++ )
    {
      vector<Point> sPt = (*itB)->getSubdivisionPoints();
      // if there is an attribute on the boundary
      Attribute* attrib = (*itB)->getAttribute();
      if( attrib )
      {
        map<string,string> prop = attrib->getProperties();
        for( map<string,string>::iterator it = prop.begin(); it!=prop.end(); it++ )
        {
          vector<int> aPts = attrib->getSymbol( it->first );
          if( aPts.size() > 0 )
          {
            for( unsigned int i=0; i<sPt.size(); i++)
            {
              glBegin(GL_LINES);
              for( unsigned int j=0; j<(aPts.size()/2)-1; j++ )
              {
                glVertex3d(scale*aPts[2*j]+sPt[i].getX(),     scale*aPts[2*j+1]+sPt[i].getY(),     0.0);
                glVertex3d(scale*aPts[2*(j+1)]+sPt[i].getX(), scale*aPts[2*(j+1)+1]+sPt[i].getY(), 0.0);
              }
              glEnd();
            }
          }
        }
      }
    }
  }
}
//-------------------------------------------------------------------------


//-------------------------------------------------------------------------
void
Model::viewDomainAttributes()
{
  double scale = 0.2*m_scaleAttribSymbol;
  // obtains the mesh points of each domain
  glLineWidth(2.0);
  list<Domain*>::iterator itD;
  for( itD = m_domains.begin(); itD != m_domains.end(); itD++ )
  {
    // if there is an attribute on the domain
    Attribute* attrib = (*itD)->getAttribute();
    if( attrib )
    {
      double x,y;
      (*itD)->getCenter(x,y);
      map<string,string> prop = attrib->getProperties();
      for( map<string,string>::iterator it = prop.begin(); it!=prop.end(); it++ )
      {
        vector<int> aPts = attrib->getSymbol( it->first );
        if( aPts.size() > 0 )
        {
          glBegin(GL_LINES);
          for( unsigned int j=0; j<(aPts.size()/2)-1; j++ )
          {
            glVertex3d(scale*aPts[2*j]+x,     scale*aPts[2*j+1]+y,     0.0);
            glVertex3d(scale*aPts[2*(j+1)]+x, scale*aPts[2*(j+1)+1]+y, 0.0);
          }
          glEnd();
        }
      }
    }
  }
}
//-------------------------------------------------------------------------


//-------------------------------------------------------------------------
void
Model::getNumSubdivOfSelectedBoundary( int& _n1, int& _n2 )
{
  list<Domain*>::iterator itD;
  for( itD = m_domains.begin(); itD != m_domains.end(); itD++ )
  {
    list<Boundary*> bounds;
    (*itD)->getBoundaries(bounds);
    list<Boundary*>::iterator itB;
    for( itB = bounds.begin(); itB != bounds.end(); itB++ )
    {
      if( (*itB)->isSelected() )
      {
        _n1 = (*itB)->getSubdivisionNumber();
      }
    }
  }
}
//-------------------------------------------------------------------------


//-------------------------------------------------------------------------
void
Model::setNumSubdivOfSelectedBoundary( int _n1, int _n2 )
{
  list<Domain*>::iterator itD;
  for( itD = m_domains.begin(); itD != m_domains.end(); itD++ )
  {
    list<Boundary*> bounds;
    (*itD)->getBoundaries(bounds);
    list<Boundary*>::iterator itB;
    for( itB = bounds.begin(); itB != bounds.end(); itB++ )
    {
      if( (*itB)->isSelected() )
      {
        if( (*itB)->getSubdivisionNumber() != _n1 )
        {
          (*itB)->setSubdivisionNumber( _n1 );
          (*itD)->cleanMesh();
        }
      }
    }
  }
}
//-------------------------------------------------------------------------


//-------------------------------------------------------------------------
void 
Model::generateMeshOfSelectedDomain()
{
  // obtains the boundary points of each domain
  list<Domain*>::iterator itD;
  for( itD = m_domains.begin(); itD != m_domains.end(); itD++ )
  {
    if( (*itD)->isSelected() )
    {
      (*itD)->generateMesh();
    }
  }
}
//-------------------------------------------------------------------------


//-------------------------------------------------------------------------
bool 
Model::applyAttributeOnSelectedBoundary( Attribute* _attrib )
{
  bool hasApplied = false;
  list<Domain*>::iterator itD;
  for( itD = m_domains.begin(); itD != m_domains.end(); itD++ )
  {
    list<Boundary*> bounds;
    (*itD)->getBoundaries(bounds);
    list<Boundary*>::iterator itB;
    for( itB = bounds.begin(); itB != bounds.end(); itB++ )
    {
      if( (*itB)->isSelected() )
      {
        (*itB)->setAttribute( _attrib );
        hasApplied = true;
      }
    }
  }
  return hasApplied;
}
//-------------------------------------------------------------------------


//-------------------------------------------------------------------------
bool 
Model::applyAttributeOnSelectedDomain( Attribute* _attrib )
{
  bool hasApplied = false;
  list<Domain*>::iterator itD;
  for( itD = m_domains.begin(); itD != m_domains.end(); itD++ )
  {
    if( (*itD)->isSelected() )
    {
      (*itD)->setAttribute( _attrib );
      hasApplied = true;
    }
  }
  return hasApplied;
}
//-------------------------------------------------------------------------


//-------------------------------------------------------------------------
void 
Model::scaleAttributeSymbol( double _scale )
{
  m_scaleAttribSymbol = _scale;
}
//-------------------------------------------------------------------------


//-------------------------------------------------------------------------
void 
Model::generateMesh()
{
  if( m_mesh )
  {
    delete m_mesh;
    m_mesh = NULL;
  }
  m_mesh = new Mesh();

  list<Domain*>::iterator itD;
  // loops over all domains to generate the mesh nodes and elements
  for( itD = m_domains.begin(); itD != m_domains.end(); itD++ )
  {
    Region* region = new Region();
    // obtains the internal points of each domain
    // to create the nodes
    map<int,int> dNodeIds;
    vector<Point> dPt = (*itD)->getTrianglesPoints();
    for( int i = 0; i < dPt.size(); i++ )
    {
      Node* nd = m_mesh->createNode( dPt[i].getX(), dPt[i].getY() );
      dNodeIds[i] = nd->getId();
    }
    // to create the elements based on the ids of the created nodes
    ElementGroup* groupD = new ElementGroup();
    vector<vector<int> > t = (*itD)->getTrianglesIncidences();
    for( int i = 0; i < t.size(); i++ )
    {
      // valid only for linear elements
      vector<int> connect;
      connect.push_back( dNodeIds[t[i][0]] );
      connect.push_back( dNodeIds[t[i][1]] );
      connect.push_back( dNodeIds[t[i][2]] );
      Element* elem = m_mesh->createElement( connect );
      groupD->addElement( elem );
    }
    region->setDomain( (*itD) );
    region->addGroup( groupD );
    // loops over all boundaries
    list<Boundary*> bounds;
    (*itD)->getBoundaries(bounds);
    list<Boundary*>::iterator itB;
    for( itB = bounds.begin(); itB != bounds.end(); itB++ )
    {
      // obtains the boundary points of each domain
      // to create the nodes
      vector<int> bNodeIds;
      vector<Point> bPt = (*itB)->getSubdivisionPoints();
      for( unsigned int i=0; i<bPt.size(); i++)
      {
        Node* nd = m_mesh->createNode( bPt[i].getX(), bPt[i].getY() );
        bNodeIds.push_back( nd->getId() );
      }
      // to create the elements based on the ids of the created nodes
      ElementGroup* groupBi = new ElementGroup();
      for( unsigned int i=0; i<bNodeIds.size()-1; i++)
      {
        // valid only for linear elements
        vector<int> connect;
        connect.push_back( bNodeIds[i] );
        connect.push_back( bNodeIds[i+1] );
        Element* elem = m_mesh->createElement( connect );
        groupBi->addElement( elem );
      }
      groupBi->setBoundary( (*itB) );
      region->addGroup( groupBi );
    }
    m_mesh->addRegion( region );
  }
}
//-------------------------------------------------------------------------

